package com.easyOrder.core.utils;

import com.easyOrder.core.json.JsonObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.easyOrder.core.exception.api.HttpClientException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * http\https请求辅助工具类
 * 
 */
public class HttpObjectRequestClient {
	private static final String _boundary = "--------httppost";

	private JsonObjectMapper objectMapper;

	private String token;

	private Class<?> responseClass;

	private TypeReference<?> responseReference;

	private Class<?> errorClass;

	private String responseFileName;

	public HttpObjectRequestClient() {
		objectMapper = new JsonObjectMapper();
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public String getResponseFileName() {
		return responseFileName;
	}

	public void setResponseFileName(String responseFileName) {
		this.responseFileName = responseFileName;
	}

	public Class<?> getResponseClass() {
		return responseClass;
	}

	public void setResponseClass(Class<?> responseClass) {
		this.responseClass = responseClass;
	}

	public TypeReference<?> getResponseReference() {
		return responseReference;
	}

	public void setResponseReference(TypeReference<?> responseReference) {
		this.responseReference = responseReference;
	}

	public Class<?> getErrorClass() {
		return errorClass;
	}

	public void setErrorClass(Class<?> errorClass) {
		this.errorClass = errorClass;
	}

	public Object execute(String uri, String method, Object requestData)
			throws HttpClientException {
		Object responseData = null;
		boolean multipartContent = requestData instanceof File;
		HttpURLConnection connection = null;
		try {
			if (uri.startsWith("https")) {
				SSLContext ctx = SSLContext.getInstance("TLS");
				ctx.init(new KeyManager[0],
						new TrustManager[] { new DefaultTrustManager() },
						new SecureRandom());
				SSLContext.setDefault(ctx);
			}
			connection = (HttpURLConnection) new URL(uri).openConnection();

			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setConnectTimeout(10000);
			connection.setRequestMethod(method);
			if (multipartContent) {
				connection.setRequestProperty("Content-Type",
						"multipart/form-data; boundary=" + _boundary);
			} else if (requestData != null) {
				connection.setRequestProperty("Content-Type",
						"application/json");
			} else {
				connection.setRequestProperty("Content-Type",
						"application/x-www-form-urlencoded");
			}
			if (token != null) {
				int sep = token.indexOf('=');
				connection.setRequestProperty(token.substring(0, sep),
						token.substring(sep + 1));
			}
			connection.setRequestProperty("Accept", "*/*");
			connection.connect();

			if (multipartContent) {
				DataOutputStream outputStream = new DataOutputStream(
						connection.getOutputStream());
				writeFile(outputStream, (File) requestData);
				outputStream.writeBytes("--" + _boundary + "--" + "\r\n");
				outputStream.writeBytes("\r\n");
			} else if (requestData != null) {
				String paramterString = objectMapper
						.writeValueAsString(requestData);
				connection.getOutputStream().write(
						paramterString.getBytes("utf-8"));
			}
			if (connection.getResponseCode() < 400) {
				InputStream inputStream = connection.getInputStream();
				if (responseClass == File.class) {
					FileOutputStream outputStream = new FileOutputStream(
							responseFileName);
					writeOutputStream(outputStream, inputStream);
					outputStream.close();
				} else if (responseClass == org.w3c.dom.Document.class) {
					DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
							.newInstance();
					responseData = documentBuilderFactory.newDocumentBuilder()
							.parse(inputStream);
				} else {
					if (responseReference != null) {
						responseData = objectMapper.readValue(inputStream,
								responseReference);
					} else {
						responseData = objectMapper.readValue(inputStream,
								responseClass);
					}
				}
			} else {
				InputStream inputStream = connection.getErrorStream();
				if (inputStream == null) {
					inputStream = connection.getInputStream();
				}
				if (connection.getResponseCode() < 500) {
					responseData = objectMapper.readValue(inputStream,
							errorClass);
				} else {
					BufferedReader reader = new BufferedReader(
							new InputStreamReader(inputStream));
					String line;
					while ((line = reader.readLine()) != null) {
						System.out.println(line);
					}
				}
			}
			connection.disconnect();
		} catch (Exception e) {
			throw new HttpClientException(e);
		}
		return responseData;
	}

	private void writeOutputStream(OutputStream outputStream,
			InputStream inputStream) {
		try {
			byte[] buffer = new byte[1024];
			int count;
			while ((count = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, count);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void writeFile(DataOutputStream outputStream, File file) {
		FileInputStream inputStream = null;
		try {
			inputStream = new FileInputStream(file);
			outputStream.writeBytes("--" + _boundary + "\r\n");
			outputStream.writeBytes("Content-Disposition: form-data; name=\""
					+ "abc\"; filename=\""
					+ URLEncoder.encode(file.getName(), "UTF-8") + "\"\r\n");
			outputStream.writeBytes("Content-Type: " + getContentType(file)
					+ "\r\n");
			outputStream.writeBytes("\r\n");
			byte[] buffer = new byte[1024];
			int count;
			while ((count = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, count);
			}
			outputStream.writeBytes("\r\n");
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private String getFileExtension(File file) {
		String name = file.getName();
		int index = name.lastIndexOf('.');
		return (index != -1) ? name.substring(index) : "";
	}

	private String getContentType(File file) {
		String fileExtension = getFileExtension(file);
		if (fileExtension.equals(".png") || fileExtension.equals(".jpg")) {
			return "image/" + fileExtension.substring(1);
		}
		return "application/octet-stream";
	}

	private static class DefaultTrustManager implements X509TrustManager {
		public void checkClientTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
		}

		public void checkServerTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
		}

		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}
	}
}
